//
//  YKRecordUtil.m
//  YK_BaseTools
//
//  Created by edward on 2019/2/20.
//

#import "YKRecordUtil.h"
#import <AVFoundation/AVFoundation.h>

/*** 日志 ***/
#ifdef DEBUG // 调试
#define YKRecordErrorLog(...) NSLog(@"❎Record error:%@ ❎",[NSString stringWithFormat:@"%@",__VA_ARGS__])
#define YKRecordLog(...) NSLog(@"⚠️Record log:%@ ⚠️",[NSString stringWithFormat:@"%@",__VA_ARGS__])
#else // 发布
#define YKRecordErrorLog(...)
#define YKRecordLog(...)
#endif

static NSString *kDefaultQualityKey = @"kDefaultQualityKey";

@interface YKRecordUtil()<AVAudioRecorderDelegate>
{
    BOOL _isHandling;
    BOOL _isCancel;
}
/// 录音机
@property (nonatomic, strong) AVAudioRecorder *recorder;

/// 录音时长
@property (nonatomic, assign) NSTimeInterval voiceSize;

/// 文件路径
@property (nonatomic, strong) NSURL *recodingPath;

/** 音量监控定时器 */
@property (nonatomic, strong) NSTimer *timer;

/** 配置 */
@property (nonatomic, assign) YKRecordQuality quality;
@property (nonatomic, assign) NSInteger sampleRate;
@property (nonatomic, assign) NSInteger bitDepth;
@property (nonatomic, assign) AVAudioQuality audioQuality;

@end

@implementation YKRecordUtil

static YKRecordUtil *_instance;

+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });

    return _instance;
}

+ (instancetype)sharedInstance
{
    if (_instance == nil) {
        _instance = [[YKRecordUtil alloc] init];
    }

    return _instance;
}


+ (BOOL)authorized
{
    AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
    if (videoAuthStatus == AVAuthorizationStatusNotDetermined)
    {
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
            
        }];
    }
    return videoAuthStatus == AVAuthorizationStatusAuthorized;
}

- (BOOL)start
{
    NSNumber *quality = [[NSUserDefaults standardUserDefaults] objectForKey:kDefaultQualityKey];
    if (!quality) {
        [self saveDefaultQuailty:YKRecordQualityNormal];
    }
    return [self startWithQuailty:quality.integerValue directory:nil];
}

- (BOOL)startWithQuailty:(YKRecordQuality)quailty directory:(NSString *)directory
{
    self.quality = quailty;
    
    // 保持亮屏
    [[UIApplication sharedApplication] setIdleTimerDisabled:true];
    
    AVAudioSession * session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
    [session setActive:YES error:nil];
    
    //录音设置
    NSMutableDictionary * recordSetting = [[NSMutableDictionary alloc]init];
    //设置录音格式
    [recordSetting  setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
    //设置录音采样率（HZ）
    [recordSetting setValue:[NSNumber numberWithFloat:self.sampleRate] forKey:AVSampleRateKey];
    //录音通道数
    [recordSetting setValue:[NSNumber  numberWithInt:2] forKey:AVNumberOfChannelsKey];
    //线性采样位数
    [recordSetting setValue:[NSNumber numberWithInteger:self.bitDepth] forKey:AVLinearPCMBitDepthKey];
    //录音的质量
    [recordSetting setValue:[NSNumber numberWithInteger:self.audioQuality] forKey:AVEncoderAudioQualityKey];
    
    //创建url
    NSString * savePath = directory ?: [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"Records"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:savePath]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:savePath withIntermediateDirectories:YES attributes:nil error:nil];
    }
    NSString *timestamp = [NSString stringWithFormat:@"%.0f",[[NSDate date] timeIntervalSince1970] * 1000];
    NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@.aac",savePath,timestamp]];
    self.recodingPath = url;
    
    if ((self.recorder && self.recorder.isRecording) || _isHandling) {
        return NO;
    }
    
    NSError * error ;
    //初始化AVAudioRecorder
    self.voiceSize = 0;
    self.recorder = [[AVAudioRecorder alloc]initWithURL:url settings:recordSetting error:&error];
    //开启音量监测
    self.recorder.meteringEnabled = YES;
    self.recorder.delegate = self;
    if(error){
        YKRecordErrorLog([NSString stringWithFormat:@"创建录音对象时发生错误，错误信息：%@",error.localizedDescription]);
        return NO;
    }
    
    __weak typeof(self) weakself = self;
    void(^startRecord)(void) = ^{
        __strong typeof(weakself) strongself = weakself;
        if([strongself.recorder prepareToRecord]){
            //开始
            [strongself.recorder record];
            strongself->_isHandling = YES;
            YKRecordLog([NSString stringWithFormat:@"🔥开始录音%@",strongself.recodingPath]);
        }
    };
    
    BOOL needHandle = NO;
    if ([self.conflictDelegate respondsToSelector:@selector(recordUtil:handleConflictIfNeed:)]) {
        needHandle = [self.conflictDelegate recordUtil:self handleConflictIfNeed:^(BOOL complete) {
            if (complete) {
                startRecord();
            }
        }];
    }
    
    // 不需要处理的话就直接开始
    if (!needHandle) {
        startRecord();
    }
    
    // 音量回调
    if ([self.delegate respondsToSelector:@selector(record:volumePercentDidChange:)]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            __weak typeof(self) weakSelf = self;
            if (self.timer) {
                [self.timer invalidate];
                self.timer = nil;
            }
            NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
                __strong typeof(weakSelf) strongSelf = weakSelf;
                [strongSelf.recorder updateMeters];
                CGFloat peak0 = pow(10, (0.01 * [strongSelf.recorder peakPowerForChannel:0]));
                CGFloat peak1 = pow(10, (0.01 * [strongSelf.recorder peakPowerForChannel:1]));
                [weakself.delegate record:weakself volumePercentDidChange:((peak0 + peak1) / 2.0)];
            }];
            self.timer = timer;
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        });
    }
    return YES;
}

- (void)pause {
    if (self.recorder) {
        [self.recorder pause];
    }
}

- (void)resume {
    if (self.recorder && !self.recorder.isRecording) {
        [self.recorder record];
    }
}

- (void)cancel {
    if (!self.recorder) return;
    if (self.recorder.isRecording) {
        _isCancel = YES;
        [self.recorder stop];
        [self.recorder deleteRecording];
        YKRecordLog([NSString stringWithFormat:@"🔥录音取消 删除音频 %@",self.recodingPath]);
    }
    [self stop];
}

- (void)stop
{
    if (!self.recorder) return;
    //获取当前录音时长
    NSTimeInterval voiceSize = self.recorder.currentTime;
    self.voiceSize = voiceSize;
    
    if (self.recorder.isRecording) {
        YKRecordLog([NSString stringWithFormat:@"🔥录音结束 = %lf %@",voiceSize,self.recodingPath]);
        [self.recorder stop];
    }
    if (self.timer) {
        [self.timer invalidate];
        self.timer = nil;
    }
    
//    if ([AVAudioSession sharedInstance].category != AVAudioSessionCategoryPlayback) {
//        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
//    }
    [[UIApplication sharedApplication] setIdleTimerDisabled:false];
}

- (void)saveDefaultQuailty:(YKRecordQuality)quality
{
    [[NSUserDefaults standardUserDefaults] setObject:@(quality) forKey:kDefaultQualityKey];
}

- (BOOL)prepareForRecord
{
    if ((self.recorder && self.recorder.isRecording) || _isHandling ) {
        return NO;
    }
    return YES;
}

- (long long)recordFileSize
{
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"Records"];
    long long sum = 0;
    if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
        NSEnumerator *childFilesEnumerator = [[[NSFileManager defaultManager] subpathsAtPath:path] objectEnumerator];
        NSString *fileName;
        while ((fileName = [childFilesEnumerator nextObject]) != nil) {
            NSString *fileAbsolutePath = [path stringByAppendingPathComponent:fileName];
            if ([[NSFileManager defaultManager] fileExistsAtPath:fileAbsolutePath]){
                sum +=  [[[NSFileManager defaultManager] attributesOfItemAtPath:fileAbsolutePath error:nil] fileSize];
            }
        }
    }
    return sum;
}

- (BOOL)clearRecordFileSize
{
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"Records"];
    NSError *err;
    if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
        NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&err];
        if (!err) {
            for (NSString *file in contents) {
                NSString *totalPath = [path stringByAppendingPathComponent:file];
                if ([[NSFileManager defaultManager] fileExistsAtPath:totalPath]) {
                    [[NSFileManager defaultManager] removeItemAtPath:totalPath error:&err];
                }
            }
        }
    }
    return err == nil;
}

#pragma mark - AVAudioRecorderDelegate

/* audioRecorderDidFinishRecording:successfully: is called when a recording has been finished or stopped. This method is NOT called if the recorder is stopped due to an interruption. */
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
    [self stopHandle];
}

- (void)stopHandle {
    _isHandling = NO;
    if (_isCancel) {
        _isCancel = NO;
        return;
    }
    [[AVAudioSession sharedInstance] setActive:NO error:nil];
    
    if ([self.delegate respondsToSelector:@selector(record:withSuccess:duration:)]) {
        [self.delegate record:self withSuccess:self.recodingPath duration:self.voiceSize];
    }
    if ([self.conflictDelegate respondsToSelector:@selector(recordUtil:recordDidfinish:)]) {
        [self.conflictDelegate recordUtil:self recordDidfinish:YES];
    }
}

/* if an error occurs while encoding it will be reported to the delegate. */
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError * __nullable)error
{
    if ([self.delegate respondsToSelector:@selector(record:withError:)]) {
        [self.delegate record:self withError:error];
    }
    if ([self.conflictDelegate respondsToSelector:@selector(recordUtil:recordDidfinish:)]) {
        [self.conflictDelegate recordUtil:self recordDidfinish:NO];
    }
}

#pragma mark - setter & getter

- (void)setQuality:(YKRecordQuality)quality
{
    _quality = quality;
    switch (quality) {
        case YKRecordQualityLow:
            self.sampleRate = 8000;
            self.bitDepth =  8;
            self.audioQuality = AVAudioQualityLow;
            break;
        case YKRecordQualityNormal:
            self.sampleRate = 16000;
            self.bitDepth =  8;
            self.audioQuality = AVAudioQualityMedium;
            break;
        case YKRecordQualityHigh:
            self.sampleRate = 32000;
            self.bitDepth = 24;
            self.audioQuality = AVAudioQualityHigh;
            break;
        case YKRecordQualityVeryHigh:
            self.sampleRate = 48000;
            self.bitDepth = 32;
            self.audioQuality = AVAudioQualityMax;
            break;
        default:
            break;
    }
}

- (YKRecordQuality)currentQuality
{
    return [[[NSUserDefaults standardUserDefaults] objectForKey:kDefaultQualityKey] integerValue];
}

@end
